/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

// models.c -- model loading and caching
// loading

// models are the only shared resource between a client and server running
// on the same machine.

/*
much of this data is only used for building vertex buffers and can be thrown away after the buffers have been built

move the buffer building code to here and keep r_mesh just for drawing

if it has the same v[1] in the base frame or the same st[0] (unadjusted) then it's assumed to be derived
from the same initial baseline so average them out (then readjust st[0] after averaging)

if it has the same v[2] or the same st[1] then it's also assumed likewise and can also be likewise averaged out

this can be done with the raw data in the mdl file
*/


#include "quakedef.h"

// these are needed for the mesh optimizer
#include <d3dx9.h>
#pragma comment (lib, "d3dx9.lib")

extern model_t	*loadmodel;
extern char	loadname[MAX_PATH];	// for hunk tags

#define NUMVERTEXNORMALS	162

float r_avertexnormals[NUMVERTEXNORMALS][3] =
{
	{-0.525731f, 0.000000f, 0.850651f}, {-0.442863f, 0.238856f, 0.864188f}, {-0.295242f, 0.000000f, 0.955423f}, {-0.309017f, 0.500000f, 0.809017f}, {-0.162460f, 0.262866f, 0.951056f}, {0.000000f, 0.000000f, 1.000000f},
	{0.000000f, 0.850651f, 0.525731f}, {-0.147621f, 0.716567f, 0.681718f}, 	{0.147621f, 0.716567f, 0.681718f}, {0.000000f, 0.525731f, 0.850651f}, {0.309017f, 0.500000f, 0.809017f}, {0.525731f, 0.000000f, 0.850651f},
	{0.295242f, 0.000000f, 0.955423f}, {0.442863f, 0.238856f, 0.864188f}, {0.162460f, 0.262866f, 0.951056f}, {-0.681718f, 0.147621f, 0.716567f}, {-0.809017f, 0.309017f, 0.500000f}, {-0.587785f, 0.425325f, 0.688191f},
	{-0.850651f, 0.525731f, 0.000000f}, {-0.864188f, 0.442863f, 0.238856f}, {-0.716567f, 0.681718f, 0.147621f}, {-0.688191f, 0.587785f, 0.425325f}, {-0.500000f, 0.809017f, 0.309017f}, {-0.238856f, 0.864188f, 0.442863f},
	{-0.425325f, 0.688191f, 0.587785f}, {-0.716567f, 0.681718f, -0.147621f}, {-0.500000f, 0.809017f, -0.309017f}, {-0.525731f, 0.850651f, 0.000000f}, {0.000000f, 0.850651f, -0.525731f}, {-0.238856f, 0.864188f, -0.442863f},
	{0.000000f, 0.955423f, -0.295242f}, {-0.262866f, 0.951056f, -0.162460f}, {0.000000f, 1.000000f, 0.000000f}, {0.000000f, 0.955423f, 0.295242f}, {-0.262866f, 0.951056f, 0.162460f}, {0.238856f, 0.864188f, 0.442863f},
	{0.262866f, 0.951056f, 0.162460f}, {0.500000f, 0.809017f, 0.309017f}, {0.238856f, 0.864188f, -0.442863f}, {0.262866f, 0.951056f, -0.162460f}, {0.500000f, 0.809017f, -0.309017f}, {0.850651f, 0.525731f, 0.000000f},
	{0.716567f, 0.681718f, 0.147621f}, {0.716567f, 0.681718f, -0.147621f}, {0.525731f, 0.850651f, 0.000000f}, {0.425325f, 0.688191f, 0.587785f}, {0.864188f, 0.442863f, 0.238856f}, {0.688191f, 0.587785f, 0.425325f},
	{0.809017f, 0.309017f, 0.500000f}, {0.681718f, 0.147621f, 0.716567f}, {0.587785f, 0.425325f, 0.688191f}, {0.955423f, 0.295242f, 0.000000f}, {1.000000f, 0.000000f, 0.000000f}, {0.951056f, 0.162460f, 0.262866f},
	{0.850651f, -0.525731f, 0.000000f}, {0.955423f, -0.295242f, 0.000000f}, {0.864188f, -0.442863f, 0.238856f}, {0.951056f, -0.162460f, 0.262866f}, {0.809017f, -0.309017f, 0.500000f}, {0.681718f, -0.147621f, 0.716567f},
	{0.850651f, 0.000000f, 0.525731f}, {0.864188f, 0.442863f, -0.238856f}, {0.809017f, 0.309017f, -0.500000f}, {0.951056f, 0.162460f, -0.262866f}, {0.525731f, 0.000000f, -0.850651f}, {0.681718f, 0.147621f, -0.716567f},
	{0.681718f, -0.147621f, -0.716567f}, {0.850651f, 0.000000f, -0.525731f}, {0.809017f, -0.309017f, -0.500000f}, {0.864188f, -0.442863f, -0.238856f}, {0.951056f, -0.162460f, -0.262866f}, {0.147621f, 0.716567f, -0.681718f},
	{0.309017f, 0.500000f, -0.809017f}, {0.425325f, 0.688191f, -0.587785f}, {0.442863f, 0.238856f, -0.864188f}, {0.587785f, 0.425325f, -0.688191f}, {0.688191f, 0.587785f, -0.425325f}, {-0.147621f, 0.716567f, -0.681718f},
	{-0.309017f, 0.500000f, -0.809017f}, {0.000000f, 0.525731f, -0.850651f}, {-0.525731f, 0.000000f, -0.850651f}, {-0.442863f, 0.238856f, -0.864188f}, {-0.295242f, 0.000000f, -0.955423f}, {-0.162460f, 0.262866f, -0.951056f},
	{0.000000f, 0.000000f, -1.000000f}, {0.295242f, 0.000000f, -0.955423f}, {0.162460f, 0.262866f, -0.951056f}, {-0.442863f, -0.238856f, -0.864188f}, {-0.309017f, -0.500000f, -0.809017f}, {-0.162460f, -0.262866f, -0.951056f},
	{0.000000f, -0.850651f, -0.525731f}, {-0.147621f, -0.716567f, -0.681718f}, {0.147621f, -0.716567f, -0.681718f}, {0.000000f, -0.525731f, -0.850651f}, {0.309017f, -0.500000f, -0.809017f}, {0.442863f, -0.238856f, -0.864188f},
	{0.162460f, -0.262866f, -0.951056f}, {0.238856f, -0.864188f, -0.442863f}, {0.500000f, -0.809017f, -0.309017f}, {0.425325f, -0.688191f, -0.587785f}, {0.716567f, -0.681718f, -0.147621f}, {0.688191f, -0.587785f, -0.425325f},
	{0.587785f, -0.425325f, -0.688191f}, {0.000000f, -0.955423f, -0.295242f}, {0.000000f, -1.000000f, 0.000000f}, {0.262866f, -0.951056f, -0.162460f}, {0.000000f, -0.850651f, 0.525731f}, {0.000000f, -0.955423f, 0.295242f},
	{0.238856f, -0.864188f, 0.442863f}, {0.262866f, -0.951056f, 0.162460f}, {0.500000f, -0.809017f, 0.309017f}, {0.716567f, -0.681718f, 0.147621f}, {0.525731f, -0.850651f, 0.000000f}, {-0.238856f, -0.864188f, -0.442863f},
	{-0.500000f, -0.809017f, -0.309017f}, {-0.262866f, -0.951056f, -0.162460f}, {-0.850651f, -0.525731f, 0.000000f}, {-0.716567f, -0.681718f, -0.147621f}, {-0.716567f, -0.681718f, 0.147621f}, {-0.525731f, -0.850651f, 0.000000f},
	{-0.500000f, -0.809017f, 0.309017f}, {-0.238856f, -0.864188f, 0.442863f}, {-0.262866f, -0.951056f, 0.162460f}, {-0.864188f, -0.442863f, 0.238856f}, {-0.809017f, -0.309017f, 0.500000f}, {-0.688191f, -0.587785f, 0.425325f},
	{-0.681718f, -0.147621f, 0.716567f}, {-0.442863f, -0.238856f, 0.864188f}, {-0.587785f, -0.425325f, 0.688191f}, {-0.309017f, -0.500000f, 0.809017f}, {-0.147621f, -0.716567f, 0.681718f}, {-0.425325f, -0.688191f, 0.587785f},
	{-0.162460f, -0.262866f, 0.951056f}, {0.442863f, -0.238856f, 0.864188f}, {0.162460f, -0.262866f, 0.951056f}, {0.309017f, -0.500000f, 0.809017f}, {0.147621f, -0.716567f, 0.681718f}, {0.000000f, -0.525731f, 0.850651f},
	{0.425325f, -0.688191f, 0.587785f}, {0.587785f, -0.425325f, 0.688191f}, {0.688191f, -0.587785f, 0.425325f}, {-0.955423f, 0.295242f, 0.000000f}, {-0.951056f, 0.162460f, 0.262866f}, {-1.000000f, 0.000000f, 0.000000f},
	{-0.850651f, 0.000000f, 0.525731f}, {-0.955423f, -0.295242f, 0.000000f}, {-0.951056f, -0.162460f, 0.262866f}, {-0.864188f, 0.442863f, -0.238856f}, {-0.951056f, 0.162460f, -0.262866f}, {-0.809017f, 0.309017f, -0.500000f},
	{-0.864188f, -0.442863f, -0.238856f}, {-0.951056f, -0.162460f, -0.262866f}, {-0.809017f, -0.309017f, -0.500000f}, {-0.681718f, 0.147621f, -0.716567f}, {-0.681718f, -0.147621f, -0.716567f}, {-0.850651f, 0.000000f, -0.525731f},
	{-0.688191f, 0.587785f, -0.425325f}, {-0.587785f, 0.425325f, -0.688191f}, {-0.425325f, 0.688191f, -0.587785f}, {-0.425325f, -0.688191f, -0.587785f}, {-0.587785f, -0.425325f, -0.688191f}, {-0.688191f, -0.587785f, -0.425325f}
};


void Mod_FixupAliasNormals (const aliashdr_t *hdr, vertexnormals_t *vnorms, aliaspolyvert_t *verts)
{
	for (int i = 0; i < hdr->numverts; i++)
	{
		// no normals initially
		Vector3Set (vnorms[i].normal, 0, 0, 0);
		vnorms[i].numnormals = 0;
	}

	const unsigned short *indexes = hdr->indexes;

	// each 3 indexes defines a triangle
	for (int i = 0; i < hdr->numindexes; i += 3, indexes += 3)
	{
		float triverts[3][3];
		float vtemp1[3], vtemp2[3], normal[3];

		// undo the vertex rotation from modelgen.c; the input verts are
		// already pre-scaled so we don't need to do that as well
		for (int j = 0; j < 3; j++)
			Vector3Set (triverts[j], verts[indexes[j]].xyz[1], -verts[indexes[j]].xyz[0], verts[indexes[j]].xyz[2]);

		// calc the per-triangle normal
		Vector3Subtract (vtemp1, triverts[0], triverts[1]);
		Vector3Subtract (vtemp2, triverts[2], triverts[1]);
		Vector3Cross (normal, vtemp1, vtemp2);
		Vector3Normalize (normal);

		// rotate the normal so the model faces down the positive x axis
		Vector3Set (normal, -normal[1], normal[0], normal[2]);

		// and accumulate it into the calculated normals array
		for (int j = 0; j < 3; j++)
		{
			Vector3Add (vnorms[indexes[j]].normal, vnorms[indexes[j]].normal, normal);
			vnorms[indexes[j]].numnormals++;
		}
	}

	// copy out normals
	for (int i = 0; i < hdr->numverts; i++)
	{
		// numnormals was checked for > 0 in modelgen.c so we shouldn't need to do it again 
		// here but we do anyway just in case a rogue modder has used a bad modelling tool
		if (vnorms[i].numnormals > 0)
		{
			Vector3Scale (verts[i].normal, (float) vnorms[i].numnormals, vnorms[i].normal);
			Vector3Normalize (verts[i].normal);
		}
		else Vector3Set (verts[i].normal, -1.0f, 0.0f, 0.0f);
	}
}


void GL_OptimizeModel (aliashdr_t *hdr, aliasmesh_t *srcmesh)
{
	int i;

	int numtris = hdr->numindexes / 3;
	unsigned short *newindexes = (unsigned short *) qScratch->FastAlloc (hdr->numindexes * sizeof (unsigned short));
	DWORD *optresult = (DWORD *) qScratch->FastAlloc ((hdr->numverts > numtris ? hdr->numverts : numtris) * sizeof (DWORD));
	DWORD *remap = (DWORD *) qScratch->FastAlloc (hdr->numverts * sizeof (DWORD));

	D3DXOptimizeFaces (hdr->indexes, numtris, hdr->numverts, FALSE, optresult);

	for (i = 0; i < numtris; i++)
	{
		int src = optresult[i] * 3;
		int dst = i * 3;

		newindexes[dst + 0] = hdr->indexes[src + 0];
		newindexes[dst + 1] = hdr->indexes[src + 1];
		newindexes[dst + 2] = hdr->indexes[src + 2];
	}

	D3DXOptimizeVertices (newindexes, numtris, hdr->numverts, FALSE, optresult);

	for (i = 0; i < hdr->numverts; i++)
	{
		// write out reordered verts
		hdr->meshverts[i].vertindex = srcmesh[optresult[i]].vertindex;
		hdr->meshverts[i].st[0] = (srcmesh[optresult[i]].st[0] + 0.5f) / (float) hdr->skinwidth;
		hdr->meshverts[i].st[1] = (srcmesh[optresult[i]].st[1] + 0.5f) / (float) hdr->skinheight;
		hdr->meshverts[i].sadjust = srcmesh[optresult[i]].sadjust;

		// build the remap table for index optimization
		remap[optresult[i]] = i;
	}

	// copy the optimized indexes back out
	for (i = 0; i < hdr->numindexes; i++)
		hdr->indexes[i] = remap[newindexes[i]];
}


void GL_MakeAliasModelDisplayLists (aliashdr_t *hdr, stvert_t *stverts, dtriangle_t *triangles)
{
	int i, j;
	int mark = qScratch->GetLowMark ();

	// temp buffer for initial building - there can never be more than this number of verts
	aliasmesh_t *mesh = (aliasmesh_t *) qScratch->FastAlloc (sizeof (aliasmesh_t) * hdr->numtris * 3);

	// there will always be this number of indexes
	hdr->indexes = (unsigned short *) gamePool->Alloc (sizeof (unsigned short) * hdr->numtris * 3);

	if (!mesh || !hdr->indexes)
	{
		Sys_Error ("GL_MakeAliasModelDisplayLists - allocation failed");
		return;
	}

	hdr->numindexes = 0;
	hdr->numverts = 0;

	trivertx_t *vert1 = hdr->frameverts[0];
	int reps = 0;

	for (int v1 = 0; v1 < hdr->vertsperframe; v1++)
	{
		for (int v2 = 0; v2 < hdr->vertsperframe; v2++)
		{
			// ignore the same vert
			if (&vert1[v1] == &vert1[v2]) continue;

			// verts are byte values so we don't need to worry about epsilons and other crap
			if (vert1[v1].v[0] != vert1[v2].v[0]) continue;
			if (vert1[v1].v[1] != vert1[v2].v[1]) continue;
			if (vert1[v1].v[2] != vert1[v2].v[2]) continue;

			reps++;
		}
	}

	if (reps) Con_Printf (PRINT_SAFE, "%i vertex repeats\n", reps);

	for (i = 0; i < hdr->numtris; i++)
	{
		for (j = 0; j < 3; j++)
		{
			int v;

			// index into hdr->vertexes
			unsigned short vertindex = triangles[i].vertindex[j];

			// basic s/t coords
			int s = stverts[vertindex].s;
			int t = stverts[vertindex].t;
			BOOL sadjust = FALSE;

			// check for back side and adjust texcoord s
			if (!triangles[i].facesfront && stverts[vertindex].onseam)
			{
				s += hdr->skinwidth / 2;
				sadjust = TRUE;
			}

			// see does this vert already exist
			for (v = 0; v < hdr->numverts; v++)
			{
				// it could use the same xyz but have different s and t
				if (mesh[v].vertindex == vertindex && (int) mesh[v].st[0] == s && (int) mesh[v].st[1] == t)
				{
					// exists; emit an index for it
					hdr->indexes[hdr->numindexes++] = v;

					// no need to check any more
					break;
				}
			}

			if (v == hdr->numverts)
			{
				// doesn't exist; emit a new vert and index
				hdr->indexes[hdr->numindexes++] = hdr->numverts;

				mesh[hdr->numverts].vertindex = vertindex;
				mesh[hdr->numverts].st[0] = s;
				mesh[hdr->numverts].st[1] = t;
				mesh[hdr->numverts++].sadjust = sadjust;
			}
		}
	}

	// create a hunk buffer for the final mesh we'll actually use
	hdr->meshverts = (aliasmesh_t *) gamePool->Alloc (sizeof (aliasmesh_t) * hdr->numverts);

	// and optimize the model
	GL_OptimizeModel (hdr, mesh);

	// build the adjacency table
	//hdr->adjacency = Mod_AdjacencyFromIndexes (hdr->indexes, hdr->numindexes, qScratch);
	//hdr->numadjacency = hdr->numindexes * 2;
	//Mod_BuildAdjacency (hdr);

	// and free our temp memory
	qScratch->FreeToLowMark (mark);
}


/*
==============================================================================

ALIAS MODELS

==============================================================================
*/

void Mod_LoadAliasBBoxes (model_t *mod, aliashdr_t *hdr)
{
	int i, j, posenum;
	aliasbbox_t *bboxes = (aliasbbox_t *) gamePool->Alloc (hdr->nummeshframes * sizeof (aliasbbox_t));

	hdr->bboxes = bboxes;

	for (posenum = 0; posenum < hdr->nummeshframes; posenum++)
	{
		trivertx_t *vert = hdr->frameverts[posenum];

		// because these are byte values we can just set to min and max for a byte then scale for the frame afterwards
		Mod_ClearBBox (bboxes[posenum].mins, bboxes[posenum].maxs);

		for (i = 0; i < hdr->vertsperframe; i++)
		{
			// we can't use Mod_AccumulateBBox because verts are stored as bytes
			for (j = 0; j < 3; j++)
			{
				if (vert[i].v[j] < bboxes[posenum].mins[j]) bboxes[posenum].mins[j] = vert[i].v[j];
				if (vert[i].v[j] > bboxes[posenum].maxs[j]) bboxes[posenum].maxs[j] = vert[i].v[j];
			}
		}

		// and finally rescale mins and maxs for the scale and scale_origin and expand slightly
		for (i = 0; i < 3; i++)
		{
			bboxes[posenum].mins[i] = ((bboxes[posenum].mins[i] * hdr->scale[i]) + hdr->scale_origin[i]) - 1.0f;
			bboxes[posenum].maxs[i] = ((bboxes[posenum].maxs[i] * hdr->scale[i]) + hdr->scale_origin[i]) + 1.0f;
		}

		// and accumulate to the whole model bbox
		Mod_AccumulateBBox (bboxes[posenum].mins, mod->mins, mod->maxs);
		Mod_AccumulateBBox (bboxes[posenum].maxs, mod->mins, mod->maxs);
	}
}


void Mod_LoadFrameVerts (model_t *mod, aliashdr_t *hdr, trivertx_t *verts, trivertx_t **frameverts)
{
	frameverts[hdr->nummeshframes] = (trivertx_t *) gamePool->Alloc (hdr->vertsperframe * sizeof (trivertx_t));

	for (int i = 0; i < hdr->vertsperframe; i++)
	{
		// just copy over positions because we're going to do normals properly later on
		frameverts[hdr->nummeshframes][i].v[0] = verts[i].v[0];
		frameverts[hdr->nummeshframes][i].v[1] = verts[i].v[1];
		frameverts[hdr->nummeshframes][i].v[2] = verts[i].v[2];
	}

	hdr->nummeshframes++;
}


/*
=================
Mod_LoadAliasFrame
=================
*/
void *Mod_LoadAliasFrame (model_t *mod, aliashdr_t *hdr, void *pin, maliasframedesc_t *frame, trivertx_t **frameverts)
{
	int            i;
	trivertx_t      *verts;
	daliasframe_t   *pdaliasframe;

	pdaliasframe = (daliasframe_t *) pin;

	strncpy (frame->name, pdaliasframe->name, 16);
	frame->firstpose = hdr->nummeshframes;
	frame->numposes = 1;

	for (i = 0; i < 3; i++)
	{
		frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i];
		frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i];
	}

	verts = (trivertx_t *) (pdaliasframe + 1);

	// load the frame vertexes
	Mod_LoadFrameVerts (mod, hdr, verts, frameverts);
	verts += hdr->vertsperframe;

	return (void *) verts;
}


/*
=================
Mod_LoadAliasGroup
=================
*/
void *Mod_LoadAliasGroup (model_t *mod, aliashdr_t *hdr, void *pin, maliasframedesc_t *frame, trivertx_t **frameverts)
{
	daliasgroup_t      *pingroup;
	int               i, numframes;
	daliasinterval_t   *pin_intervals;
	void            *ptemp;

	pingroup = (daliasgroup_t *) pin;
	numframes = LittleLong (pingroup->numframes);
	frame->firstpose = hdr->nummeshframes;
	frame->numposes = numframes;

	for (i = 0; i < 3; i++)
	{
		frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
		frame->bboxmax.v[i] = pingroup->bboxmax.v[i];
	}

	pin_intervals = (daliasinterval_t *) (pingroup + 1);
	frame->interval = LittleFloat (pin_intervals->interval);
	pin_intervals += numframes;
	ptemp = (void *) pin_intervals;

	for (i = 0; i < numframes; i++)
	{
		Mod_LoadFrameVerts (mod, hdr, (trivertx_t *) ((daliasframe_t *) ptemp + 1), frameverts);
		ptemp = (trivertx_t *) ((daliasframe_t *) ptemp + 1) + hdr->vertsperframe;
	}

	return ptemp;
}


//=========================================================

/*
=================
Mod_FloodFillSkin

Fill background pixels so mipmapping doesn't have haloes - Ed
=================
*/

typedef struct
{
	short		x, y;
} floodfill_t;

extern unsigned d_8to24table_rgba[];

// must be a power of 2
#define FLOODFILL_FIFO_SIZE 0x1000
#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
floodfill_t	floodfill_fifo[FLOODFILL_FIFO_SIZE];

#define FLOODFILL_STEP(off, dx, dy) \
{ \
	if (pos[off] == fillcolor) \
	{ \
		pos[off] = 255; \
		floodfill_fifo[inpt].x = x + (dx), floodfill_fifo[inpt].y = y + (dy); \
		inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
	} \
	else if (pos[off] != 255) fdc = pos[off]; \
}

void Mod_FloodFillSkin (byte *skin, int skinwidth, int skinheight)
{
	byte fillcolor = *skin; // assume this is the pixel to fill
	int inpt = 0, outpt = 0;
	int filledcolor = -1;

	if (filledcolor == -1)
	{
		filledcolor = 0;

		// attempt to find opaque black
		for (int i = 0; i < 256; ++i)
		{
			if (d_8to24table_rgba[i] == (255 << 0)) // alpha 1.0
			{
				filledcolor = i;
				break;
			}
		}
	}

	// can't fill to filled color or to transparent color (used as visited marker)
	if ((fillcolor == filledcolor) || (fillcolor == 255))
	{
		//printf( "not filling skin from %d to %d\n", fillcolor, filledcolor );
		return;
	}

	floodfill_fifo[inpt].x = 0, floodfill_fifo[inpt].y = 0;
	inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;

	while (outpt != inpt)
	{
		int x = floodfill_fifo[outpt].x, y = floodfill_fifo[outpt].y;
		int fdc = filledcolor;
		byte *pos = &skin[x + skinwidth * y];

		outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;

		if (x > 0)				FLOODFILL_STEP (-1, -1, 0);
		if (x < skinwidth - 1)	FLOODFILL_STEP (1, 1, 0);
		if (y > 0)				FLOODFILL_STEP (-skinwidth, 0, -1);
		if (y < skinheight - 1)	FLOODFILL_STEP (skinwidth, 0, 1);

		skin[x + skinwidth * y] = fdc;
	}
}


void Mod_LoadIndividualSkin (aliashdr_t *hdr, aliasskin_t *skin, byte *data, char *name)
{
	Mod_FloodFillSkin (data, hdr->skinwidth, hdr->skinheight);

	// save 8 bit texels for the player model to remap
	skin->SkinTexels = (byte *) gamePool->Alloc (hdr->skinwidth * hdr->skinheight);
	memcpy (skin->SkinTexels, data, hdr->skinwidth * hdr->skinheight);

	skin->Texture[0] = D3D_LoadTexture (name, data, hdr->skinwidth, hdr->skinheight, TEX_MIPMAP | TEX_NODISCARD);
	skin->Texture[1] = D3D_LoadTexture (name, data, hdr->skinwidth, hdr->skinheight, TEX_MIPMAP | TEX_LUMA | TEX_NODISCARD);
}


/*
===============
Mod_LoadAllSkins
===============
*/
void *Mod_LoadAllSkins (aliashdr_t *hdr, int numskins, daliasskintype_t *pskintype)
{
	int i, j;
	int groupskins;
	daliasskingroup_t *pinskingroup;
	daliasskininterval_t *pinskinintervals;

	if (numskins < 1 || numskins > MAX_SKINS)
		Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);

	hdr->skins = (aliasskindef_t *) gamePool->Alloc (numskins * sizeof (aliasskindef_t));

	for (i = 0; i < numskins; i++)
	{
		if (pskintype->type == ALIAS_SKIN_SINGLE)
		{
			// no animation, no intervals
			hdr->skins[i].type = ALIAS_SKIN_SINGLE;
			hdr->skins[i].numintervals = 0;
			hdr->skins[i].intervals = NULL;
			hdr->skins[i].skins = (aliasskin_t *) gamePool->Alloc (sizeof (aliasskin_t));

			// and load the skin
			Mod_LoadIndividualSkin (hdr, hdr->skins[i].skins, (byte *) (pskintype + 1), va ("%s_%i", loadmodel->name, i));
			pskintype = (daliasskintype_t *) ((byte *) (pskintype + 1) + hdr->skinwidth * hdr->skinheight);
		}
		else
		{
			// animating skin group.  yuck.
			pskintype++;
			pinskingroup = (daliasskingroup_t *) pskintype;
			groupskins = LittleLong (pinskingroup->numskins);
			pinskinintervals = (daliasskininterval_t *) (pinskingroup + 1);

			pskintype = (daliasskintype_t *) (pinskinintervals + groupskins);

			hdr->skins[i].type = ALIAS_SKIN_GROUP;
			hdr->skins[i].numintervals = groupskins;
			hdr->skins[i].intervals = (float *) gamePool->Alloc (groupskins * sizeof (float));
			hdr->skins[i].skins = (aliasskin_t *) gamePool->Alloc (groupskins * sizeof (aliasskin_t));

			for (j = 0; j < groupskins; j++)
			{
				// store out this interval
				hdr->skins[i].intervals[j] = pinskinintervals[j].interval;

				// and load the skin
				Mod_LoadIndividualSkin (hdr, &hdr->skins[i].skins[j], (byte *) pskintype, va ("%s_%i_%i", loadmodel->name, i, j));
				pskintype = (daliasskintype_t *) ((byte *) pskintype + hdr->skinwidth * hdr->skinheight);
			}
		}
	}

	return (void *) pskintype;
}


void Mod_LoadAllFrames (model_t *mod, aliashdr_t *hdr, daliasframetype_t *pframetype)
{
	trivertx_t **frameverts = NULL;
	QSCRATCHLOCKER Lock ((void **) &frameverts, __FUNCTION__);

	hdr->nummeshframes = 0;

	// FIXME: do this right (consider it done)
	Mod_ClearBBox (mod->mins, mod->maxs);

	for (int i = 0; i < hdr->numframes; i++)
	{
		if (pframetype->type == ALIAS_SINGLE)
			pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (mod, hdr, pframetype + 1, &hdr->frames[i], frameverts);
		else pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (mod, hdr, pframetype + 1, &hdr->frames[i], frameverts);
	}

	// and now copy the pointers out to the model
	hdr->frameverts = (trivertx_t **) gamePool->Alloc (hdr->nummeshframes * sizeof (trivertx_t *));
	memcpy (hdr->frameverts, frameverts, hdr->nummeshframes * sizeof (trivertx_t *));
}


//=========================================================================

/*
=================
Mod_LoadAliasModel
=================
*/
void Mod_LoadAliasModel (model_t *mod, void *buffer)
{
	mdl_t *pinmodel = (mdl_t *) buffer;
	int version = LittleLong (pinmodel->version);

	if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION);

	// allocate space for a working header
	aliashdr_t *hdr = (aliashdr_t *) gamePool->Alloc (sizeof (aliashdr_t) + LittleLong (pinmodel->numframes) * sizeof (maliasframedesc_t));

	mod->flags = LittleLong (pinmodel->flags);
	mod->type = mod_alias;

	// endian-adjust and copy the data, starting with the alias model header
	hdr->boundingradius = LittleFloat (pinmodel->boundingradius);
	hdr->numskins = LittleLong (pinmodel->numskins);
	hdr->skinwidth = LittleLong (pinmodel->skinwidth);
	hdr->skinheight = LittleLong (pinmodel->skinheight);
	hdr->vertsperframe = LittleLong (pinmodel->numverts);
	hdr->numtris = LittleLong (pinmodel->numtris);
	hdr->numframes = LittleLong (pinmodel->numframes);
	hdr->numverts = hdr->numtris * 3;

	// validate the setup
	// Sys_Error seems a little harsh here...
	if (hdr->numframes < 1) Host_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", hdr->numframes);
	if (hdr->numtris <= 0) Host_Error ("model %s has no triangles", mod->name);
	if (hdr->vertsperframe <= 0) Host_Error ("model %s has no vertices", mod->name);

	hdr->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
	mod->synctype = (synctype_t) LittleLong (pinmodel->synctype);
	mod->numframes = hdr->numframes;

	Vector3Copy (hdr->scale, pinmodel->scale);
	Vector3Copy (hdr->scale_origin, pinmodel->scale_origin);

	// load the skins (need to do it this way because we don't know the number in advance)
	daliasskintype_t *pskintype = (daliasskintype_t *) &pinmodel[1];
	pskintype = (daliasskintype_t *) Mod_LoadAllSkins (hdr, hdr->numskins, pskintype);

	// base s and t vertices and triangle lists come directly from the on-disk version
	// this horribleness is to just jump ahead to the correct offset for frames
	stvert_t *pinstverts = (stvert_t *) pskintype;
	dtriangle_t *pintriangles = (dtriangle_t *) &pinstverts[hdr->vertsperframe];

	// load the frames - this is the last call and no longer advances the pointers
	Mod_LoadAllFrames (mod, hdr, (daliasframetype_t *) &pintriangles[hdr->numtris]);

	// build the draw lists
	GL_MakeAliasModelDisplayLists (hdr, pinstverts, pintriangles);

	// build the adjacency table
	hdr->adjacency = Mod_AdjacencyFromTriangles (mod, pintriangles, hdr->numtris, hdr->vertsperframe, gamePool);
	hdr->numadjacency = hdr->numtris * 6;

	// and finally we can load bounding boxes for the individual frames
	Mod_LoadAliasBBoxes (mod, hdr);

	// and fix up the whole model bbox
	for (int i = 0; i < 3; i++)
	{
		// this was already rescaled when accumulating so just clamp it here
		if (mod->mins[i] > -16) mod->mins[i] = -16;
		if (mod->maxs[i] < 16) mod->maxs[i] = 16;
	}

	// and save out the alias header
	mod->aliashdr = hdr;

	// muzzleflash colour flags and other hard-coded crapness
	if (!strcmp (&mod->name[6], "wizard.mdl")) mod->flags |= EF_WIZARDFLASH;
	if (!strcmp (&mod->name[6], "shalrath.mdl")) mod->flags |= EF_SHALRATHFLASH;
	if (!strcmp (&mod->name[6], "shambler.mdl")) mod->flags |= EF_SHAMBLERFLASH;
	if (!strcmp (&mod->name[6], "eyes.mdl")) mod->flags |= EF_EYES;
}


